const { Matrix4, Vector2, Vector3, Vector4 } = require("./matrix");
const { getWebglContext, initShaderProgram, vsSource, fsSource } = require("./util");
import "../css/index.css";
import { pointMaxSize, pointMinSize,pointNum,blackHoleImagePath } from "./config";

/** @type {WebGLRenderingContext}  */
var gl = null;
/** @type {CanvasRenderingContext2D} */
var ctx2d = null;

//缓冲区
var vertexBuffer = null;
var colorBuffer = null;
var sizeBuffer = null;

//纹理句柄
var textureHandl = null;
//程序属性索引
const programInfo = {
    program: null,
    attribute: {
        a_Position: null,
        _vec2: null,
        pointColor: null,
        pointSize: null
    },
    uniformAttrib: {
        orthMat: null,
        texture: null,
        modelMat: null,
        modelMat2: null,
        color: null,
        isModel: null,
        Modelcolor: null
    }
}
/**@type {Matrix4} 物理模型矩阵*/
const modelMat = new Matrix4();
/**@type {Matrix4} 物理模型矩阵*/
const modelMat2 = new Matrix4();
/**@type {Vector2} 位置 */
const position = new Vector2();

var scalecount = 0;

var backColor = new Vector4(0.1, 0.1, 0.1, 1);
const backColor_MAX = new Vector4(0.1, 0.1, 0.1, 1);
const backColor_MIN = new Vector4(0, 0, 0, 1);
const randomColor = new Vector4();
const color = new Vector4();
let deltaTime = 0.01;


const points = [];
const targetPoints = [];


const blackHoleImage = new Image();
blackHoleImage.src = blackHoleImagePath;
let globalAlpha = 0;
let isAdd3 = true;

let canvas = null;
let canvas2 = null;

const orthView = new Vector4(-30, 30, -30, 30);


function main() {
    if (!init()) {
        return
    }
    setConfig();
    requestAnimationFrame(anim);
}

function anim(time) {
    deltaTime = 14;
    drawBlackHole();
    Backupdata();
    drawBack();
    modelupdata();

    requestAnimationFrame(anim);
}

function drawBlackHole() {
    if (ctx2d) {
        const cw = canvas2.width;
        const ch = canvas2.height;
        const w = 100 + 200 * globalAlpha;
        const h = 100 + 200 * globalAlpha;
        ctx2d.clearRect(-1 * w, -1 * h, canvas2.width, canvas2.height)
        if (isAdd3) {
            globalAlpha += 0.001;
            isAdd3 = globalAlpha < 1;
        } else {
            globalAlpha -= 0.001;
            isAdd3 = globalAlpha < 0.5;
        }

        ctx2d.rotate(-0.03 * globalAlpha);
        ctx2d.globalAlpha = globalAlpha;
        ctx2d.drawImage(blackHoleImage, 0, 0, blackHoleImage.width, blackHoleImage.height, -w / 2, -h / 2, w, h);
    }
}

function init() {
    gl = getWebglContext(canvas);
    programInfo.program = initShaderProgram(gl, vsSource, fsSource);
    if (!programInfo.program) {
        return false;
    }
    gl.useProgram(programInfo.program);
    //获取属性索引
    {
        programInfo.attribute._vec2 = gl.getAttribLocation(programInfo.program, '_vec2');
        programInfo.attribute.a_Position = gl.getAttribLocation(programInfo.program, 'a_Position');
        programInfo.attribute.pointColor = gl.getAttribLocation(programInfo.program, 'inpointColor');
        programInfo.attribute.pointSize = gl.getAttribLocation(programInfo.program, 'pointSize');

        programInfo.uniformAttrib.modelMat = gl.getUniformLocation(programInfo.program, 'modelMat')
        programInfo.uniformAttrib.modelMat2 = gl.getUniformLocation(programInfo.program, 'modelMat2')
        programInfo.uniformAttrib.orthMat = gl.getUniformLocation(programInfo.program, 'orthMat');
        programInfo.uniformAttrib.texture = gl.getUniformLocation(programInfo.program, 'texture');

        programInfo.uniformAttrib.color = gl.getUniformLocation(programInfo.program, "color");
        programInfo.uniformAttrib.isModel = gl.getUniformLocation(programInfo.program, "isModel");
        programInfo.uniformAttrib.Modelcolor = gl.getUniformLocation(programInfo.program, "Modelcolor");
        //属性值获取
    }
    //创建缓冲区
    {
        vertexBuffer = gl.createBuffer();
        textureHandl = gl.createTexture();
        colorBuffer = gl.createBuffer();
        sizeBuffer = gl.createBuffer();
    }
    return true;
}

function setConfig() {
    //设置正交视口
    const orthmat = new Matrix4();
    orthmat.setOrtho(orthView.x, orthView.y, orthView.z, orthView.w, 10, 0)
    gl.uniformMatrix4fv(programInfo.uniformAttrib.orthMat, false, orthmat.elements)
    // modelMat.scale(2,2,1);
    // modelMat.concat(new Matrix4().setTranslate(1,1,0));
    // modelMat.translate(.5,.5,0)

    //设置背景颜色
    gl.clearColor(backColor.x, backColor.y, backColor.z, backColor.w);

    //绑定数据
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(getRandomPosints(pointNum)), gl.STATIC_DRAW);

    //设置数据
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.vertexAttribPointer(programInfo.attribute.a_Position, 3, gl.FLOAT, false, 0, 0);//vertex

    gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(getRandomColor(pointNum)), gl.STATIC_DRAW);
    gl.vertexAttribPointer(programInfo.attribute.pointColor, 3, gl.FLOAT, false, 0, 0);//color

    gl.bindBuffer(gl.ARRAY_BUFFER, sizeBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(getRandomSize(pointNum, pointMinSize, pointMaxSize)), gl.STATIC_DRAW);
    gl.vertexAttribPointer(programInfo.attribute.pointSize, 1, gl.FLOAT, false, 0, 0);//color

    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.uniform1i(programInfo.uniformAttrib.texture, 0);

    //启动设置
    gl.enableVertexAttribArray(programInfo.attribute.a_Position);
    gl.enableVertexAttribArray(programInfo.attribute.pointColor);
    gl.enableVertexAttribArray(programInfo.attribute.pointSize);
    gl.activeTexture(gl.TEXTURE0);
    // 启动透明
    gl.enable(gl.BLEND);
    gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_CONSTANT_ALPHA);


    setInterval(() => {
        updataBackColor();
    }, 100000);
    updataBackColor();
}



function drawBack() {
    gl.vertexAttrib2f(programInfo.attribute._vec2, 0, 0)
    gl.bindTexture(gl.TEXTURE_2D, textureHandl)
    gl.clearDepth(1.0);
    gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
    gl.drawArrays(gl.POINTS, 0, pointNum);
}

function modelupdata() {
    gl.uniform1f(programInfo.uniformAttrib.isModel, 1.0);
    colorUpdata();
    posUpdata();
}
function Backupdata() {
    gl.uniform1f(programInfo.uniformAttrib.isModel, 0.0);
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(getRandomPosints(pointNum)), gl.STATIC_DRAW);
    gl.vertexAttribPointer(programInfo.attribute.a_Position, 3, gl.FLOAT, false, 0, 0);//vertex
    gl.uniformMatrix4fv(programInfo.uniformAttrib.modelMat, false, modelMat.elements);
}

/**
 * 
 * @param {number} count 
 */
function getRandomPosints(count) {
    const arr = [];
    if (points.length == 0) {
        for (let i = 0; i < count; i++) {
            const point = new Vector2(5 * (Math.random() - .5) * orthView.x, 5 * (Math.random() - .5) * orthView.z);
            const target = new Vector2(0.01 * (Math.random() - .5), 0.01 * (Math.random() - .5));
            points.push(point)
            targetPoints.push(target);
            arr.push(point.x);
            arr.push(point.y);
            arr.push(0);
        }
        return arr;
    }
    let dir = null;
    for (let i = 0; i < count; i++) {
        /** @type {Vector2} */
        const point = points[i];
        /** @type {Vector2} */
        const target = targetPoints[i];

        dir = new Vector2(-point.x, -point.y).normalize();
        const r = point.magnitude();
        const g = 0.002 + 0.002 * globalAlpha;
        const v = g * deltaTime / (r * r);
        dir.ride(v);
        target.x += dir.x;
        target.y += dir.y;
        point.x += target.x * deltaTime;
        point.y += target.y * deltaTime;

        // Vector2.rotate(point,new Vector2(1,1),0.01);

        arr.push(point.x);
        arr.push(point.y);
        arr.push(0);
        if (Vector2.magnitude(Vector2.zero, point) < globalAlpha * 2 + 2 || point.magnitude > orthView.x * 1.3) {
            point.x = (Math.random()) * orthView.x;
            point.y = (Math.random()) * orthView.z;
            if (getRandomBool()) point.x = -point.x;
            if (getRandomBool()) point.y = -point.y;
            target.x = 0.01 * (Math.random() - .5);
            target.y = 0.01 * (Math.random() - .5);
        }
    }
    return arr;

}
const getRandomBool = () => Math.random() > .5;

function getRandomColor(count) {
    const arr = [];
    for (let i = 0; i < count; i++) {
        arr.push(Math.random());
        arr.push(Math.random());
        arr.push(Math.random());
    }
    return arr;
}
function getRandomSize(count, minSize, maxSize) {
    const arr = [];
    for (let i = 0; i < count; i++) {
        arr.push((Math.random() * (maxSize - minSize) + minSize));
    }
    return arr;
}

function posUpdata() {
    modelMat2.setIdentity();
    const vec = Vector2.sub(new Vector2(0, 0), position);
    //关键
    modelMat2.translate(vec.x * (scalecount / 100) / 1, vec.y * (scalecount / 100) / 1, 0);
    modelMat2.scale(1 + scalecount / 100, 1 + scalecount / 100, 1);
}

function colorUpdata() {
    color.lerp(randomColor, 0.01);
    if (Vector4.sub(color, randomColor).magnitude() < 0.1) {
        setRandomColor();
    }
    gl.uniform4f(programInfo.uniformAttrib.Modelcolor, color.x, color.y, color.z, 1);
}

function setRandomColor() {
    randomColor.x = Math.random();
    randomColor.y = Math.random();
    randomColor.z = Math.random();
    if (Vector3.sub(randomColor, backColor).magnitude() < 0.1) {
        setRandomColor();
    }
}


/**
 * 根据时间改变背景颜色
 */
function updataBackColor() {
    const date = new Date();
    const houre = date.getHours();
    const process = houre >= 12 ? (houre - 12) / 12 : (12 - houre) / 12;
    backColor = Vector4.lerp(backColor_MAX, backColor_MIN, process);
    gl.clearColor(backColor.x, backColor.y, backColor.z, backColor.w);

}


function loadDiv() {
    const divList = document.getElementsByClassName("black-hole-effect");
    let currItem = null;
    for (let i = 0; i < divList.length; i++) {
        let item = divList[i];
        if (item.tagName == 'DIV'||item.tagName == 'div') {
            currItem = item;
            
            break;
        }
    }
    if(currItem==null){
        console.warn("黑洞特效:没有找到(div,class=black-hole-effect)的dom")
        return
    }

    const canvsElement  = document.createElement('canvas');
    canvsElement.className = "black-hole-effect-canvas1";
    currItem.append(canvsElement);
    canvas = canvsElement;
    const canvsElement2  = document.createElement('canvas');
    canvsElement2.className = "black-hole-effect-canvas2";
    currItem.append(canvsElement2);
    canvas2 = canvsElement2;
    canvas.width=canvas2.width=900;
    canvas.height=canvas2.height=550;
    ctx2d = canvas2.getContext('2d');
    ctx2d.translate(canvas2.width / 2, canvas2.height / 2);

    console.info("黑洞特效加载成功");
    main();
}

try{
    loadDiv();
}catch(e){
    console.error("黑洞特效加载异常:"+e);
}